﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using ALDIOrganizer.Properties;

namespace ALDIOrganizer
{
    /* This part of the class deals with headers of the datagridview.
     * Their pFormat is customizable in pParameters.
     * The application does not provide UI for the user to easily customize the header,
     * but the way I programmed it ensures that adding such a feature would be quick and easy.
     
     * HEADER FORMAT GUIDE:
     * 
     * - the header pFormat pattern is a string, which consists of keywords separated with a semi-colon (;)
     * - those keywords are listed in the headerFormatKeywords string array.
     * - any string in between semi-colons which is not a keyword, will be treated literally by the application.
     * - the header'pShift length (not the pFormat pattern'pShift, but the actual resulting header'pShift length!) cannot exceed
     * which by default is 48 characters.
     * 
     * KEYWORDS USED:
     *      
     * "Day" - Day of the week (eg. "Monday").
     * "n" - new line.
     * "cn" - conditional new line. if the NEXT bit is rendered to an Empty string, this one is ignored.     * 
     * "date" - date in short pFormat. only works for rotas, not templates (since templates do not exist "in Time"!!)
     * "ppl" - how many emps_from_database work On this particular Day (from minimum to maximum).     
     * "hrs" - GetTotal number of hours for the Day
     * "inf" - special information about the Day: it could be "Inventory" or "Bank Holiday" etc. */
    
    public partial class SheetView
    {
        private string _headerFormat;
        public string headerFormat
        {
            get { return _headerFormat; }
        }

        private void ReadHeaderFormat()
        {
            switch (Mode)
            {
                case SheetType.Rota:
                    {
                        _headerFormat = Settings.Default.HEADERFORMAT_rota;
                        break;
                    }
                case SheetType.Template:
                    {
                        _headerFormat = Settings.Default.HEADERFORMAT_template;
                        break;
                    }
                default:
                    break;
            }
        }

        // recognizable keywords. any other string will be treated literally
        private static string[] headerFormatKeywords
        {
            get { return new[] { "day", "n", "cn", "date", "ppl", "hrs", "inf" }; }
        }

        private bool headerFormatLiteral(string s)
        {
            return (!headerFormatKeywords.Contains(s));
        }

        private string getEmp()
        {
            return Resources.DATAGRID_Header_Employee;
        }

        private string getDay(int col)
        {
            return Time.NameOfDay(col);
        }

        private string getN()
        {
            return Environment.NewLine;
        }

        private string getCN(string next_bit)
        {
            return (next_bit == String.Empty) ? String.Empty : Environment.NewLine;
        }

        private string getDate(int col)
        {
            if (Mode != SheetType.Rota)
                return String.Empty;
            return refillRota.Monday.AddDays(col).Day.ToString("d2") +
                   "." +
                   refillRota.Monday.AddDays(col).Month.ToString("d2");
        }

        private string getPpl(int col)
        {
            var result = String.Empty;

            var allShifts = refill.GetAllShiftsForColumn(col);
            var min_max = Shift.min_maxEmployees(allShifts, refill_template.WorkingHours[col-1]);            
            sbyte from = min_max.Key;
            sbyte to = min_max.Value;

            if ((from < 0) || (to < 0))
            {
                throw new ApplicationException("DataGridViewExtended.getPpl retrieved invalid values from Shifts.min_maxEmployees " + from.ToString() + " / " + to.ToString() + "  - both should have been greater than 0 but it isn't so");
            }

            if ((from + to) == 0)
                result = "noone";
            else
            {
                var noun = (Math.Max(from, to) > 1) ? " people" : " person";
                if (from == to)
                    result = from.ToString() + noun;                
                else
                    result = String.Concat(from.ToString(), " to ", to.ToString(), noun);
            }            

            return result;
        }
        
        private string getHrs(int col)
        {
            return refill.TotalHours()[col].GetTotal + " hrs";            
        }

        // not implemented yet
        private string getInf(int col)
        {
            if (Mode == SheetType.Template)
                return String.Empty;
            var _status = refillRota.DayStatuses[col - 1];

            switch (_status)
            {       
                case SpecialDayStatus.Inventory:
                    return Resources.DATAGRID_Header_Status_Inventory;                    
                case SpecialDayStatus.Shut:
                    return Resources.DATAGRID_Header_Status_Shut;
                default:
                    return String.Empty;                
            }
        }

        private string ComposeColumnHeader(int col)
        {
            var elements = headerFormat.Split(';');
            Array.Reverse(elements);
            var result = String.Empty;
            var following_bit = String.Empty; // needed for cn. this is the reason why we revert it, too
            
            foreach (var element in elements)
            {
                var bit_to_add = String.Empty;
                if (headerFormatLiteral(element))
                    bit_to_add = element;
                else
                {
                    switch (element.ToLower())
                    {                        
                        case "day": bit_to_add = getDay(col-1); break;
                        case "n": bit_to_add = getN(); break;
                        case "cn": bit_to_add = getCN(following_bit); break;
                        case "date": bit_to_add = getDate(col - 1); break;
                        case "ppl": bit_to_add = getPpl(col); break;                        
                        case "hrs": bit_to_add = getHrs(col - 1); break;
                        case "inf": bit_to_add = getInf(col); break;                        
                        
                        default: break;
                    }
                }

                result = bit_to_add + result;

                following_bit = bit_to_add;
            }

            return result;
        }

        private void RefreshColumnHeader(int col)
        {   
            Columns[col].HeaderText = (col == 0) ?
                getEmp() :
                ComposeColumnHeader(col);
            AutoResizeColumnHeadersHeight();
        }

        private void RefreshEmployeesHours(Int64 empID)
        {
            if (Mode == SheetType.Template) return;
            
            refill[empID][0].Update(false, true);            
        }

        internal void RefreshEmployeesRow(Int64 empID)
        {
            if ((!Bound) | (Mode == SheetType.Template))
            {
                return;
            }
            refillRota.RefreshEmployeeRow(empID);
            OnRefreshTotalHours((SumOfHours?)refill.TotalHours()[7],
                (refill.Mode == SheetType.Rota) ?
                (SumOfHours?)refillRota.GetSumOfContractHours :
                (SumOfHours?)null);
        }


        public void Display()
        {
            if (!Bound)
            {
                return;
            }
            SuspendLayout();

            if (Mode == SheetType.Rota)
            {
                /* this will retrieve "fresh" Employees info from the CurrentFile
                 * and force the sheet to adjust its rows accordingly */
                refillRota.Refresh();
            }
            // now the sheet has got all "correct" rows

            DeactivateEvents();
            Rows.Clear();

            var tags = new List<RowTag>();
            foreach (var row in refill.GetAllRows)
            {
                var tag = row[0].Tag;
                UpdateCells(AddRowAndReturnIt(tag));
                tags.Add(tag);
            }
            

            if (Mode == SheetType.Rota)
            {
                Rows.Add(SpawnNewRow(new RowTag(-1))); // bottom bar
                refillRota.UpdateBottomBar(new[] { Weekday.Monday, Weekday.Tuesday, Weekday.Wednesday, Weekday.Thursday, Weekday.Friday, Weekday.Saturday, Weekday.Sunday });
            }

            SortTemplate(); // it checks by itself whether we are in the right GetParentMode
            ResumeLayout();
            Refill_OnDataChanged(tags.ToArray(), Time.AllWeekdays);
            ActivateEvents();
            
        }

        
    }
}